#include <sdk.h>
#include <algorithm>
#include <wx/listctrl.h>
#include <configmanager.h>
#include <M_Code_editor.h>
#include <editormanager.h>
#include <configurationpanel.h>
#include <wx/listctrl.h>
#include "M_Code_colourmanager.h"
#include "M_Code_StyledTextCtrl.h"
#include "OccurrencesHighlighting.h"
#include "highlighter.h"
#include "occurrencespanel.h"
#include "occurrenceshighlightingconfigurationpanel.h"

namespace {
  PluginRegistrant<OccurrencesHighlighting> reg( _T( "OccurrencesHighlighting" ) );
  const int idViewOccurencesPanel = XRCID( "idViewOccurencesPanel" );
  const int idMenuEntryPermanent = wxNewId();
  const int idMenuEntryRemove = wxNewId();
  const int idContextRemove = wxNewId();
}

BEGIN_EVENT_TABLE( OccurrencesHighlighting, M_Code_Plugin )
  EVT_MENU( idViewOccurencesPanel, OccurrencesHighlighting::OnViewOccurrencesPanel )
  EVT_UPDATE_UI( idViewOccurencesPanel, OccurrencesHighlighting::OnUpdateViewMenu )
END_EVENT_TABLE()

OccurrencesHighlighting::OccurrencesHighlighting():
  m_pHighlighter( NULL ),
  m_pPanel( NULL ),
  m_pViewMenu( NULL ) {
  if( !Manager::LoadResource( _T( "OccurrencesHighlighting.zip" ) ) ) {
    NotifyMissingFile( _T( "OccurrencesHighlighting.zip" ) );
  }
  ColourManager* cm = Manager::Get()->GetColourManager();
  cm->RegisterColour( _T( "Editor" ), _T( "Highlight occurrence" ), _T( "editor_highlight_occurrence" ), *wxRED );
  cm->RegisterColour( _T( "Editor" ), _T( "Permanently highlighted occurrences" ), _T( "editor_highlight_occurrence_permanently" ), *wxGREEN );
}

OccurrencesHighlighting::~OccurrencesHighlighting() {
}

void OccurrencesHighlighting::OnAttach() {
  m_pHighlighter = new Highlighter( m_texts );
  EditorHooks::HookFunctorBase *editor_hook = new EditorHooks::HookFunctor<OccurrencesHighlighting>( this, &OccurrencesHighlighting::OnEditorHook );
  m_FunctorId = EditorHooks::RegisterHook( editor_hook );
  m_pPanel = new OccurrencesPanel( Manager::Get()->GetAppWindow() );
  CodeBlocksDockEvent dockevt( cbEVT_ADD_DOCK_WINDOW );
  dockevt.name = _T( "HighlightedOccurrences" );
  dockevt.title = _T( "Highlighted Occurrences" );
  dockevt.pWindow = m_pPanel;
  dockevt.minimumSize.Set( 50, 50 );
  dockevt.desiredSize.Set( 150, 100 );
  dockevt.floatingSize.Set( 100, 150 );
  dockevt.dockSide = CodeBlocksDockEvent::dsLeft;
  dockevt.stretch = true;
  Manager::Get()->ProcessEvent( dockevt );
  m_pPanel->GetListCtrl()->Connect( wxEVT_COMMAND_LIST_KEY_DOWN, wxListEventHandler( OccurrencesHighlighting::OnListKeyDown ), NULL, this );
  Connect( idMenuEntryPermanent, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( OccurrencesHighlighting::OnHighlightPermanently ), NULL, this );
  Connect( idMenuEntryRemove,    wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( OccurrencesHighlighting::OnHighlightRemove ),      NULL, this );
  m_pPanel->GetListCtrl()->Connect( wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( OccurrencesHighlighting::OnPanelPopupMenu ), NULL, this );
  Connect( idContextRemove, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( OccurrencesHighlighting::OnRemove ), NULL, this );
}

void OccurrencesHighlighting::OnRelease( bool appShutDown ) {
  EditorHooks::UnregisterHook( m_FunctorId );
  delete m_pHighlighter;
  m_pHighlighter = NULL;
  m_pPanel->GetListCtrl()->Disconnect( wxEVT_COMMAND_LIST_KEY_DOWN, wxListEventHandler( OccurrencesHighlighting::OnListKeyDown ), NULL, this );
  Disconnect( idMenuEntryPermanent, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( OccurrencesHighlighting::OnHighlightPermanently ), NULL, ( wxEvtHandler* )this );
  Disconnect( idMenuEntryRemove,    wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( OccurrencesHighlighting::OnHighlightRemove ),      NULL, ( wxEvtHandler* )this );
  m_pPanel->GetListCtrl()->Disconnect( wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( OccurrencesHighlighting::OnPanelPopupMenu ), NULL, this );
  Disconnect( idContextRemove, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( OccurrencesHighlighting::OnRemove ), NULL, this );
  if( m_pPanel && !appShutDown ) {
    CodeBlocksDockEvent docevt( cbEVT_REMOVE_DOCK_WINDOW );
    docevt.pWindow = m_pPanel;
    Manager::Get()->ProcessEvent( docevt );
    m_pPanel->Destroy();
    m_pPanel = NULL;
  }
}

void OccurrencesHighlighting::BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data ) {
  if( !IsAttached() ) {
    return;
  }
  if( type != mtEditorManager || !menu ) {
    return;
  }
  EditorManager* emngr = Manager::Get()->GetEditorManager();
  if( !emngr ) {
    return;
  }
  EditorBase *edb = emngr->GetActiveEditor();
  if( !edb || !edb->IsBuiltinEditor() ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ( ( M_Code_Editor* )edb )->GetControl();
  if( !stc ) {
    return;
  }
  wxString word = GetWordAtCaret();
  if( word.IsEmpty() ) {
    return;
  }
  if( m_texts.find( word ) == m_texts.end() ) {
    menu->Append( idMenuEntryPermanent, _T( "高亮标记 '" ) + word + _T( "'" ) );
  } else {
    menu->Append( idMenuEntryRemove, _T( "取消高亮 '" ) + word + _T( "'" ) );
  }
}

void OccurrencesHighlighting::OnViewOccurrencesPanel( wxCommandEvent& event ) {
  CodeBlocksDockEvent evt( event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW );
  evt.pWindow = m_pPanel;
  Manager::Get()->ProcessEvent( evt );
}

void OccurrencesHighlighting::OnUpdateViewMenu( wxUpdateUIEvent &event ) {
  if( m_pViewMenu ) {
    bool isVis = IsWindowReallyShown( ( wxWindow* )m_pPanel );
    m_pViewMenu->Check( idViewOccurencesPanel, isVis );
  }
  event.Skip();
}

void OccurrencesHighlighting::OnListKeyDown( wxListEvent &event ) {
  switch( event.GetKeyCode() ) {
    case WXK_DELETE:
      RemoveSelected();
      break;
    case WXK_INSERT:
    default:
      event.Skip();
  }
}

wxString OccurrencesHighlighting::GetWordAtCaret()const {
  M_Code_Editor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    M_Code_StyledTextCtrl *control = ed->GetControl();
    if( control ) {
      wxString selectedText = control->GetSelectedText();
      if( selectedText.IsEmpty() ||
          selectedText.Contains( _T( " " ) ) || selectedText.Contains( _T( "\t" ) ) ) {
        const int pos = control->GetCurrentPos();
        const int ws = control->WordStartPosition( pos, true );
        const int we = control->WordEndPosition( pos, true );
        selectedText = control->GetTextRange( ws, we );
      }
      return selectedText;
    }
  }
  return wxEmptyString;
}

void OccurrencesHighlighting::OnHighlightPermanently( wxCommandEvent& ( event ) ) {
  wxString word = GetWordAtCaret();
  m_texts.insert( word );
  m_pHighlighter->TextsChanged();
  UpdatePanel();
}

void OccurrencesHighlighting::OnHighlightRemove( wxCommandEvent& ( event ) ) {
  wxString word = GetWordAtCaret();
  m_texts.erase( word );
  m_pHighlighter->TextsChanged();
  UpdatePanel();
}

void OccurrencesHighlighting::UpdatePanel() {
  wxListCtrl *list = m_pPanel->GetListCtrl();
  list->Freeze();
  list->DeleteAllItems();
  wxListItem item;
  for( std::set<wxString>::iterator it = m_texts.begin(); it != m_texts.end(); it++ ) {
    item.SetText( *it );
    item.SetId( std::max( list->GetItemCount(), 0 ) );
    list->InsertItem( item );
  }
  list->Thaw();
}

void OccurrencesHighlighting::OnPanelPopupMenu( wxContextMenuEvent& ( event ) ) {
  if( m_pPanel->GetListCtrl()->GetSelectedItemCount() > 0 ) {
    wxMenu *menu = new wxMenu;
    menu->Append( idContextRemove, _T( "Remove" ), _T( "" ) );
    m_pPanel->GetListCtrl()->PopupMenu( menu );
  }
}

void OccurrencesHighlighting::OnRemove( wxCommandEvent& ( event ) ) {
  RemoveSelected();
}

void OccurrencesHighlighting::RemoveSelected() {
  long item;
  item = m_pPanel->GetListCtrl()->GetNextItem( -1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  while( item != -1 ) {
    m_texts.erase( m_pPanel->GetListCtrl()->GetItemText( item ) );
    m_pPanel->GetListCtrl()->DeleteItem( item );
    item = m_pPanel->GetListCtrl()->GetNextItem( item - 1,
           wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
  }
  m_pHighlighter->TextsChanged();
}

void OccurrencesHighlighting::OnEditorHook( M_Code_Editor* editor, wxScintillaEvent& event ) {
  m_pHighlighter->Call( editor, event );
}

cbConfigurationPanel* OccurrencesHighlighting::GetConfigurationPanel( wxWindow* parent ) {
  return new OccurrencesHighlightingConfigurationPanel( parent );
}
